// PJ Game - pixeljoint.com - Bodhi Peace
// TAGS in source
// [TOGGLE] important pieces to turn on/off
// [DEBUG] debug only code
// [WORK] work on this!
// [HACK] don't look

// C headers
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <ctime>

// External
#include <cml/cml.h>

#include "glee.h"
#include "GL/glfw.h"

#include "GLFT_Font.hpp"

//#include "fmod.hpp"
#include "fmod.hpp"
#include "fmod_errors.h"

// Local Headers
////////////////////////////
#include "global.hpp"
#include "common.hpp"
#include "filemanager.hpp"
#include "log.hpp"
////////////////////////////
#include "statemanager.hpp"
#include "inifile.hpp"
#include "bitfont.hpp"
#include "staticimage.hpp"
////////////////////////////

// Variables
KEG::IniFile configFile;
//KEG::BitFont testFont;
//::GLFT_Font ttfFont;
//StaticImage logo;
int    running=1;

FMOD_RESULT result;
FMOD::System *fmodSystem;

// CALLBACKS ////////////////////
int GLFWCALL windowCloseStop()
{
	// stop user from killing OGL context
	// so we can do it ourselves grrrr....
	running = false;
	return GL_FALSE;
}
void GLFWCALL windowResize( int width, int height )
{
	using namespace Global::Window;
	Width = width;
	Height = height;
	// viewport
	if(Global::Game::Resize)
	{
		glViewport( 0, 0, Global::Window::Width, Global::Window::Height );
	}
	else
	{
		using namespace Global;		
		int x = (Width - Game::Width)/2;
		int y = (Height - Game::Height)/2;

		glViewport( x, y, (GLsizei)Game::Width, (GLsizei)Game::Height );
	}
	//LOG_RAW() << "EVENT: Window Resize to " << Width << 'x' << Height << std::endl;
}

// FUNCTIONS
void Render()
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


	StateManager::Render();
	//glEnable( GL_ALPHA_TEST );
	//testFont.RenderTextCentered( "ABCDEFG", 200, Global::Game::Width );
	
	//glDisable( GL_ALPHA_TEST );
	//glDisable( GL_TEXTURE_RECTANGLE_ARB );
	//glEnable( GL_BLEND );
	//ttfFont.drawText( 100, 100, "ABCDEFG" );
	//glDisable( GL_BLEND );
	//glEnable( GL_TEXTURE_RECTANGLE_ARB );
	//glEnable( GL_ALPHA_TEST );

	//logo.Render( 300, 100 );

	// Swap front and back buffers (we use a double buffered display)
	glfwSwapBuffers();

	assert( glGetError() == GL_NO_ERROR );	
}
void Update()
{
	static double timeElapsed = 0;

	timeElapsed = glfwGetTime();
	glfwSetTime(0);

	if( timeElapsed > 0.1 )
	{
		timeElapsed = 0.1;
	}

	StateManager::Update( timeElapsed );

	fmodSystem->update();
}
void InitSound()
{
	LOG_INFO( "Starting Sound System" );
	result = FMOD::System_Create(&fmodSystem);		// Create the main system object.
	if (result != FMOD_OK)
	{
		//printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
		//exit(1);
		LOG_ERROR( "Sound System Failed" );
		LOG_RAW() << "FMOD Error " << result << " " << FMOD_ErrorString(result) << std::endl;
	}

	LOG_INFO( "Initializing Sound System" );
	result = fmodSystem->init(100, FMOD_INIT_NORMAL, 0);	// Initialize FMOD.
	if (result != FMOD_OK)
	{
		//printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
		//exit(1);
		LOG_ERROR( "Sound System Failed" );
		LOG_RAW() << "FMOD Error " << result << " " << FMOD_ErrorString(result) << std::endl;
	}
	//ERRCHECK(result);

}
void InitGame()
{
	StateManager::Init();
	//testFont.Load( KEG::Path( FONT, "white" ) );
	//testFont.Reset( 0,0, 2 );

	//ttfFont.open( KEG::Path( TTF, "temp" ), 32 );

	//logo.LoadFullImage( KEG::Path( IMAGE, "pixeljoint" ) );
	//logo.Height( 2*logo.Height() );
	//logo.Width( 2*logo.Width() );
	//assert( ttfFont.isValid() );
	// background
	//glGenTextures( 1, &backtex );
	//glBindTexture( GL_TEXTURE_RECTANGLE_ARB, backtex );
	//glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	//glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
	
	//GLFWimage img;
	//memset( &img, 0, sizeof(img) );
	//glfwReadImage( "backgrid.tga", &img, GLFW_NO_RESCALE_BIT);

	//glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, img.Width, img.Height,
	//	0, GL_RGBA, GL_UNSIGNED_BYTE, img.Data );

	//glfwFreeImage( &img );
}
void Exit()
{
	StateManager::Release();
	KEG::Texture::ReleaseAll();
	assert( FMOD_OK == fmodSystem->release() );
	//
	//testFont.Release();

	//ttfFont.release();
	//glDeleteTextures( 1, &backtex );
	//Box::Release();

}
void InitOGL()
{
	glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
	glClearColor(0.0f,0.0f,0.0f,0.0f);
	glClearDepth( 1.0 );

	// CAMERA
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	glOrtho( 0, 640, 480,0, -1, 1 );
	//gluPerspective( 45, 640.0/480.0, 1.0, 50.0 );
	//glFrustum( -15.0f, 115.0f, 0.0f, 100.0f, 1.0, 50.0 );

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();

	//glViewport( 0, 0, 640, 480 );
	windowResize( Global::Window::Width, Global::Window::Height );

	// ENABLES
	//glEnable( GL_DEPTH_TEST );
	glDepthFunc( GL_GREATER );
	glClearDepth( 0.0 );

	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	glEnable( GL_ALPHA_TEST );
	glAlphaFunc( GL_GREATER, 0.5f );

	//glEnable( GL_BLEND );
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glActiveTexture( GL_TEXTURE0 );
	glEnable( GL_TEXTURE_2D );
	glEnable( GL_TEXTURE_RECTANGLE_ARB );

	//glEnable( GL_CULL_FACE );
	//glFrontFace( GL_CCW );
	//glCullFace( GL_BACK );

	//glEnableClientState( GL_TEXTURE_COORD_ARRAY );
	//glEnableClientState( GL_VERTEX_ARRAY );
	//glEnableClientState( GL_NORMAL_ARRAY );

	//glEnable(GL_LIGHTING);
	//glEnable(GL_LIGHT0);

	//glEnable(GL_COLOR_MATERIAL);
	//glEnable(GL_NORMALIZE);

	assert( glGetError() == GL_NO_ERROR );

}
void LoadSettings()
{
	configFile.Read( "config.ini" );
	//assert( configFile.Good() );
	if( configFile.Good() )
		LOG_INFO( "Found INI File" );
	else
		LOG_WARN( "INI File Not Found" );

	using namespace Global::Window;
#define windowSect "window"
	Width = configFile.GetInt( windowSect, "width", Width );
	Height = configFile.GetInt( windowSect, "height", Height );
	if( 32 == configFile.GetInt( windowSect, "bpp", 32 ) )
	{
		using namespace Global::Window;
		Green = Red = Blue = 8;
		Alpha = 8;
	}
	else if( 24 == configFile.GetInt( windowSect, "bpp", 24 ) )
	{
		using namespace Global::Window;
		Green = Red = Blue = 8;
		Alpha = 0;
	}
	else
	{
		using namespace Global::Window;
		Red = Blue = 5;
		Green = 6;
		Alpha = 0;
	}
	Fullscreen = configFile.GetInt( windowSect, "fullscreen", Fullscreen );
	Stencil = configFile.GetInt( windowSect, "stencil", Stencil );
	Depth = configFile.GetInt( windowSect, "depth", Depth );
	Vsync = configFile.GetInt( windowSect, "vsync", Vsync );

	// Game Vars
	Global::Game::Resize = configFile.GetInt( "game", "stretch", Global::Game::Resize );

	// Now Directory Tree
	KEG::Dir( BASE, configFile.GetString( "path", "base" ) );
	KEG::Dir( IMAGE, configFile.GetString( "path", "image" ) );
	KEG::Ext( IMAGE, ".png" );
	KEG::LinkDir( TTF, IMAGE );
	KEG::Ext( TTF, ".ttf" );
	KEG::Dir( XML,  configFile.GetString( "path", "xml" ) );
	KEG::Ext( XML, ".xml" );
	KEG::LinkDir( FONT, XML );
	KEG::Ext( FONT, ".font.xml" );
	KEG::LinkDir( FRAME, XML );
	KEG::Ext( FRAME, ".frame.xml" );
	KEG::Dir( MUSIC, configFile.GetString( "path", "music" ) );
	KEG::Ext( MUSIC, ".mp3" );
	KEG::LinkDir( LEVEL, XML );
	KEG::Ext( LEVEL, ".lvl.xml" );
}
void LogWindow()
{
	// Attempted Window Parameters
	{
		using namespace Global::Window;
		LOG_RAW() << "Window Desired " 
			<< Width << 'x' << Height << ' '
			<< 'R' << Red << 'G' << Green
			<< 'B' << Blue << 'A' << Alpha
			<< " D" << Depth << 'S' << Stencil
			<< (Fullscreen?" Fullscreen":" Windowed")
			<< std::endl;
	}
	// Verify
	using namespace Global::Window;
	glGetIntegerv(GL_RED_BITS, &Red);
	glGetIntegerv(GL_BLUE_BITS, &Blue);
	glGetIntegerv(GL_GREEN_BITS, &Green);
	glGetIntegerv(GL_ALPHA_BITS, &Alpha);
	glGetIntegerv(GL_DEPTH_BITS, &Depth);
	glGetIntegerv(GL_STENCIL_BITS, &Stencil);
	glfwGetWindowSize( &Width, &Height );
	LOG_RAW() << "Window Created " 
		<< Width << 'x' << Height << ' '
		<< 'R' << Red << 'G' << Green
		<< 'B' << Blue << 'A' << Alpha
		<< " D" << Depth << 'S' << Stencil
		<< (Fullscreen?" Fullscreen":" Windowed")
		<< std::endl;
}
void SaveSettings()
{
	configFile.Write();
}

// MAIN /////////////////////////
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
					 LPSTR lpCmdLine, int nCmdShow)
{
	int    ok;             // Flag telling if the window was opened

	LOG_INIT( "PJGame v." Global_VersionString );

	// Initialize GLFW
	glfwInit();

	// Load INI
	LoadSettings();

	// Open window
	using namespace Global;
	ok = glfwOpenWindow(
		Window::Width, Window::Height,
		Window::Red, Window::Blue, Window::Green,
		Window::Alpha,
		Window::Depth,
		Window::Stencil,
		Window::Fullscreen?GLFW_FULLSCREEN:GLFW_WINDOW
		);

	// If we could not open a window, exit now
	if( !ok )
	{
		glfwTerminate();
		return 0;
	}

	// Verify the window
	LogWindow();

	// Set window title
	glfwSetWindowTitle( "PJGame" Global_VersionString );
	// Callbacks
	glfwSetWindowCloseCallback( windowCloseStop );
	glfwSetWindowSizeCallback( windowResize );
	glfwSetKeyCallback( StateManager::KeyPress );

	if(Window::Vsync) LOG_INFO( "Enabling Vertical Sync" );
	else LOG_INFO( "Disabling Vertical Sync" );
	glfwSwapInterval( Window::Vsync );

	// seed random
	srand( time(0) );

	// Sound
	InitSound();

	// OPENGL INIT
	InitOGL();

	// GAME INIT
	InitGame();

	// Enable sticky keys
	// glfwEnable( GLFW_STICKY_KEYS );

	// Main rendering loop
	glfwSetTime( 0 );
	while(running)
	{
		// Update
		Update();

		// Call our rendering function
		Render();

		// Check if the escape key was pressed, or if the window was closed
		//running = !glfwGetKey( GLFW_KEY_ESC ) && running;
			//glfwGetWindowParam( GLFW_OPENED );
	}

	// exit
	Exit();

	// NOW close the window
	glfwCloseWindow();

	// Terminate GLFW
	glfwTerminate();

	//
	SaveSettings();

	return 0;
}
